package org.chartsy.nvi; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Stroke; import java.text.DecimalFormat; import java.util.LinkedHashMap; import org.chartsy.main.ChartFrame; import org.chartsy.main.chart.Indicator; import org.chartsy.main.data.DataItem; import org.chartsy.main.data.Dataset; import org.chartsy.main.utils.DefaultPainter; import org.chartsy.main.utils.Range; import org.chartsy.main.utils.SerialVersion; import org.chartsy.main.utils.StrokeGenerator; import org.openide.nodes.AbstractNode; /** * * @author Viorel */ public class NormalizedVolatilityIndicator extends Indicator { private static final long serialVersionUID = SerialVersion.APPVERSION; public static final String NVI = "nvi"; public static final String MA65 = "ma65"; public static final String MA200 = "ma200"; private IndicatorProperties properties; public NormalizedVolatilityIndicator() { super(); properties = new IndicatorProperties(); } public String getName() { return "Normalized Volatility Indicator"; } public String getLabel() { return properties.getLabel() + " (" + properties.getPeriod() + ")"; } public String getPaintedLabel(ChartFrame cf) { return getLabel(); } public Indicator newInstance() { return new NormalizedVolatilityIndicator(); } public LinkedHashMap getHTML(ChartFrame cf, int i) { LinkedHashMap ht = new LinkedHashMap(); DecimalFormat df = new DecimalFormat("#,##0.00"); double[] values = getValues(cf, i); String[] labels = {"NVI:","MA (65):","MA (200):"}; ht.put(getLabel(), " "); if (values.length > 0) { Color[] colors = getColors(); for (int j = 0; j < values.length; j++) { ht.put(getFontHTML(colors[j], labels[j]), getFontHTML(colors[j], df.format(values[j]))); } } return ht; } public @Override Range getRange(ChartFrame cf) { Dataset vpiDataset = visibleDataset(cf, NVI); Dataset ma65Dataset = visibleDataset(cf, MA65); Dataset ma200Dataset = visibleDataset(cf, MA200); double min = vpiDataset.getCloseAt(0); double max = vpiDataset.getCloseAt(0); for (int i = 0; i < vpiDataset.getItemsCount(); i++) { if (vpiDataset.getDataItem(i) != null) { if (vpiDataset.getCloseAt(i) > 0) { max = Math.max(max, vpiDataset.getCloseAt(i)); min = Math.min(min, vpiDataset.getCloseAt(i)); } } if (ma65Dataset.getDataItem(i) != null) { if (ma65Dataset.getCloseAt(i) > 0) { max = Math.max(max, ma65Dataset.getCloseAt(i)); min = Math.min(min, ma65Dataset.getCloseAt(i)); } } if (ma200Dataset.getDataItem(i) != null) { if (ma200Dataset.getCloseAt(i) > 0) { max = Math.max(max, ma200Dataset.getCloseAt(i)); min = Math.min(min, ma200Dataset.getCloseAt(i)); } } } if (min > 1.343) { min = 1.3; } if (max < 1.343) { max = 1.4; } return new Range(min, max); } public void paint(Graphics2D g, ChartFrame cf, Rectangle bounds) { Dataset vpiDataset = visibleDataset(cf, NVI); Dataset ma65Dataset = visibleDataset(cf, MA65); Dataset ma200Dataset = visibleDataset(cf, MA200); if (vpiDataset != null) { if (maximized) { Range range = getRange(cf); DefaultPainter.line(g, cf, range, bounds, vpiDataset, properties.getColor(), properties.getStroke()); if (ma65Dataset != null && properties.getMA65Visibility()) { DefaultPainter.line(g, cf, range, bounds, ma65Dataset, properties.getMA65Color(), properties.getMA65Stroke()); } if (ma200Dataset != null && properties.getMA200Visibility()) { DefaultPainter.line(g, cf, range, bounds, ma200Dataset, properties.getMA200Color(), properties.getMA200Stroke()); } } } } public void calculate() { Dataset mainDataset = getDataset(); if (mainDataset != null && !mainDataset.isEmpty()) { Dataset vpiDataset = getVPIDataset(getDataset()); addDataset(NVI, vpiDataset); addDataset(MA65, Dataset.SMA(vpiDataset, properties.getMA65Period())); addDataset(MA200, Dataset.SMA(vpiDataset, properties.getMA200Period())); } } public boolean hasZeroLine() { return false; } public boolean getZeroLineVisibility() { return false; } public Color getZeroLineColor() { return null; } public Stroke getZeroLineStroke() { return null; } public boolean hasDelimiters() { return true; } public boolean getDelimitersVisibility() { return true; } public double[] getDelimitersValues() { return new double[] {new Double(1.343)}; } public Color getDelimitersColor() { return new Color(0x204a87); } public Stroke getDelimitersStroke() { return StrokeGenerator.getStroke(0); } public Color[] getColors() { return new Color[] { properties.getColor(), properties.getMA65Color(), properties.getMA200Color() }; } public double[] getValues(ChartFrame cf) { Dataset vpiDataset = visibleDataset(cf, NVI); Dataset ma65Dataset = visibleDataset(cf, MA65); Dataset ma200Dataset = visibleDataset(cf, MA200); int i = vpiDataset.getLastIndex(); double[] values = new double[3]; values[0] = vpiDataset.getDataItem(i) != null ? vpiDataset.getCloseAt(i) : 0; values[1] = ma65Dataset.getDataItem(i) != null ? ma65Dataset.getCloseAt(i) : 0; values[2] = ma200Dataset.getDataItem(i) != null ? ma200Dataset.getCloseAt(i) : 0; return values; } public double[] getValues(ChartFrame cf, int i) { Dataset vpiDataset = visibleDataset(cf, NVI); Dataset ma65Dataset = visibleDataset(cf, MA65); Dataset ma200Dataset = visibleDataset(cf, MA200); double[] values = new double[3]; values[0] = vpiDataset.getDataItem(i) != null ? vpiDataset.getCloseAt(i) : 0; values[1] = ma65Dataset.getDataItem(i) != null ? ma65Dataset.getCloseAt(i) : 0; values[2] = ma200Dataset.getDataItem(i) != null ? ma200Dataset.getCloseAt(i) : 0; return values; } public boolean getMarkerVisibility() { return properties.getMarker(); } public AbstractNode getNode() { return new IndicatorNode(properties); } private Dataset getVPIDataset(Dataset dataset) { int period = properties.getPeriod(); int count = dataset.getItemsCount(); Dataset result = Dataset.EMPTY(count); double[] highLow = new double[count]; // high - low double[] highPrevClose = new double[count]; // high - prev close double[] lowPrevClose = new double[count]; // low - prev close double[] trueRange = new double[count]; // daily TR for (int i = 0; i < count; i++) { double high = dataset.getHighAt(i); double low = dataset.getLowAt(i); if (i == 0) { trueRange[i] = high - low; // first TR value = high - low } else { double prevClose = dataset.getCloseAt(i - 1); highLow[i] = high - low; highPrevClose[i] = high - prevClose; lowPrevClose[i] = low - prevClose; trueRange[i] = Math.max(highLow[i], Math.max(highPrevClose[i], lowPrevClose[i])); if (i >= period - 1) { double close = dataset.getCloseAt(i); double atrValue = 0; for (int j = 0; j < period; j++) { atrValue += trueRange[i - j]; } atrValue /= (double)period; double value = (atrValue / close) * 100; result.setDataItem(i, new DataItem(dataset.getTimeAt(i), value)); } } } return result; } public @Override Double[] getPriceValues(ChartFrame cf) { Range range = getRange(cf); int max = (int) range.getUpperBound() + 1; Double[] values = new Double[max + 1]; for (int i = 0; i < max; i++) values[i] = new Double((double) i); values[max] = 1.343D; return values; } }